// ExplorerStyle Header

/****************************************************************************
**
** Copyright (c) 2007 Trolltech ASA <info@trolltech.com>
**
** Use, modification and distribution is allowed without limitation,
** warranty, liability or support of any kind.
**
** explorerstyle.cpp - version 1.0
****************************************************************************/

/*
 * QWindowsNativeStyle.cpp  0.4.0 / Nov 17, 2010
 *
 * Copyright (c) 2010 - 2011, cute4j.com and contributors.
 *
 * This software is licensed under the LGPL version 2.1 license.
 * A copy of this license is included in the file LICENSE-LGPL.txt.
 */
#include "QWindowsNativeStyle.h"

#include <QtGui>
#include <qt_windows.h>
#include <uxtheme.h>

// QWindowsNativeStyle :: Win32 Functions. -------------------------------------

typedef HANDLE HTHEME;
typedef HANDLE (WINAPI *FpOpenThemeData)(HWND hwnd, LPCWSTR pszClassList);
typedef HRESULT (WINAPI *FpDrawThemeBackground)(HANDLE hTheme, HDC hdc,
                                                int iPartId, int iStateId,
                                                const RECT *pRect,
                                                OPTIONAL const RECT *pClipRect);

static FpDrawThemeBackground pDrawThemeBackground = 0;
static FpOpenThemeData pOpenThemeData = 0;

void drawRebarBackground(const QRect &rect, QPainter *painter) {
  if (!rect.isEmpty()) {
    QString cacheKey = "q_rebar_" + QString::number(rect.size().width()) + 'x' +
                       QString::number(rect.size().height());
    QPixmap pixmap;
    if (!QPixmapCache::find(cacheKey, pixmap)) {
      pixmap = QPixmap(rect.size());    
      const RECT wRect = {0, 0, pixmap.width(), pixmap.height()};
      HBITMAP bitmap = pixmap.toWinHBITMAP();
      HDC hdc = CreateCompatibleDC(qt_win_display_dc());
      HGDIOBJ oldhdc = (HBITMAP)SelectObject(hdc, bitmap);
      HTHEME theme = pOpenThemeData(0, L"REBAR");
      pDrawThemeBackground(theme, hdc, 0, 0, &wRect, NULL);
      pixmap = pixmap.fromWinHBITMAP(bitmap);
      SelectObject(hdc, oldhdc);
      DeleteObject(bitmap);
      DeleteDC(hdc);
      QPixmapCache::insert(cacheKey, pixmap);
    }
    painter->drawPixmap(rect.topLeft(), pixmap);
  }
}

// QWindowsNativeStyle :: Public Constructor. ----------------------------------

/*!
 * \brief Constructs new QWindowsNativeStyle object.
 */
QWindowsNativeStyle::QWindowsNativeStyle()
  : QWindowsVistaStyle()
{
  if (sm_appThemed && !(sm_patchDisabled || pDrawThemeBackground)) {
    QLibrary themeLib("uxtheme");
    themeLib.load();
    if (themeLib.isLoaded()) {
      pDrawThemeBackground =
                (FpDrawThemeBackground)themeLib.resolve("DrawThemeBackground");
      pOpenThemeData = (FpOpenThemeData)themeLib.resolve("OpenThemeData");
    }
  }
}

// QWindowsNativeStyle :: Public Member Function. ------------------------------

/*!
 * \brief Initializes QWindowsNativeStyle class.
 *
 * \param appThemed \c true if themed
 */
void QWindowsNativeStyle::initialize(bool appThemed)
{
  if (!sm_initialized) {
    sm_initialized = true;
    sm_appThemed = appThemed;
    QWindowsNativeStyle::titleBarFont = QApplication::font("Q3TitleBar");
    sm_vista = ((QSysInfo::WindowsVersion >= QSysInfo::WV_VISTA) && 
                (QSysInfo::WindowsVersion < QSysInfo::WV_NT_based));
  }
}

// QWindowsNativeStyle :: Overridden Member Functions. -------------------------

/*!
 * \inheritDoc
 */
void QWindowsNativeStyle::drawPrimitive(PrimitiveElement element,
                                        const QStyleOption *option,
                                        QPainter *painter,
                                        const QWidget *widget) const
{
  if (sm_patchDisabled) {
    if (sm_vista) {
      QWindowsVistaStyle::drawPrimitive(element, option, painter, widget);
    } else {
      QWindowsXPStyle::drawPrimitive(element, option, painter, widget);
    }
    return;
  }
  switch (element) {
    case PE_IndicatorMenuCheckMark:
      if (sm_vista) {
        QWindowsVistaStyle::drawPrimitive(element, option, painter, widget);
        break;
      }
      if (sm_appThemed) {
        if (const QStyleOptionMenuItem *menuItem =
                      qstyleoption_cast<const QStyleOptionMenuItem *>(option)) {
          if (menuItem->checkType == QStyleOptionMenuItem::Exclusive) {
            painter->save();
            QRect checkRect(option->rect.left() + 4,
                            option->rect.center().y() - 2, 13, 13);
            QColor color;
            if (menuItem->state & QStyle::State_On) {
              color = option->palette.highlightedText().color();
            } else {
              color = option->palette.text().color();
            }
            painter->setPen(color);
            painter->fillRect(checkRect.x() + 1, checkRect.y() + 1, 4, 4,
                              color);
            painter->drawEllipse(checkRect.x(), checkRect.y(), 5, 5);
            painter->restore();
            break;
          }
        }
      }
      QWindowsStyle::drawPrimitive(element, option, painter, widget);
      break;
    case PE_IndicatorToolBarHandle:
      if (!sm_appThemed) {
        painter->save();
        painter->translate(option->rect.x(), option->rect.y());
        if (option->state & State_Horizontal) {
          int x;
          if (QApplication::layoutDirection() == Qt::RightToLeft) {
            x = option->rect.width() - 1;
          } else {
            x = 2;
          }
          if (option->rect.height() > 4) {
            qDrawShadePanel(painter, x, 2, 3, option->rect.height() - 4,
                            option->palette, false, 1, 0);
          }
        } else {
          if (option->rect.width() > 4) {
            qDrawShadePanel(painter, 2, 2, option->rect.width() - 4, 3,
                            option->palette, false, 1, 0);
          }
        }
        painter->restore();
      } else {
        if (sm_vista) {
          QWindowsVistaStyle::drawPrimitive(element, option, painter, widget);
        } else {
          QWindowsXPStyle::drawPrimitive(element, option, painter, widget);
        }
      }
      break;
    case PE_Widget:
      if (sm_appThemed && sm_explorerStyle) {
        if (QMainWindow *window =
                               qobject_cast<QMainWindow *>(widget->window())) { 
          QRegion topreg;
          QRegion bottomreg;
          QMenuBar* menubar = qFindChild<QMenuBar *>(window);
          //We draw the menubar as part of the top toolbar area
          if (menubar) {
            QRect rect(menubar->mapToParent(menubar->rect().topLeft()),
                                            menubar->rect().size());
            topreg += rect;
          }
          //We need the bounding rect for all toolbars
          QList<QToolBar *> toolbars = qFindChildren<QToolBar *>(window);
          for (int i = 0; i < toolbars.size(); i++) {
            QToolBar *tb = toolbars.at(i);
            if (!tb->isFloating()) {
              QRect rect(tb->mapToParent(tb->rect().topLeft()),
                                         tb->rect().size());
              if (window->toolBarArea(tb) == Qt::TopToolBarArea) {
                topreg += rect;
              } else if (window->toolBarArea(tb) == Qt::BottomToolBarArea) {
                bottomreg += rect;
              }
            }
          }
          //This is a hack to workaround missing toolbar updates since
          //we now require updates that span across the whole toolbar area:
          QRect topRect = topreg.boundingRect();
          topRect.setWidth(window->width());
          if (m_currentTopRect != topRect) {
            m_currentTopRect = topRect;
            window->update(topRect);
          }
          QRect bottomRect = bottomreg.boundingRect();
          bottomRect.setWidth(window->width());
          if (m_currentBottomRect != bottomRect) {
            m_currentBottomRect = bottomRect;
            window->update(bottomRect);
          }
          //Fill top toolbar area with gradient
          drawRebarBackground(topRect, painter);
          //Fill bottom toolbar area with gradient
          drawRebarBackground(bottomRect, painter);
          break;
        }
      } //fall through
    default: 
      if (sm_vista) {
        QWindowsVistaStyle::drawPrimitive(element, option, painter, widget);
      } else {
        QWindowsXPStyle::drawPrimitive(element, option, painter, widget);
      }
      break;
  }
}

/*!
 * \inheritDoc
 */
void QWindowsNativeStyle::drawControl(ControlElement element,
                                      const QStyleOption *option,
                                      QPainter *painter,
                                      const QWidget *widget) const
{
  if (sm_patchDisabled) {
    if (sm_vista) {
      QWindowsVistaStyle::drawControl(element, option, painter, widget);
    } else {
      QWindowsXPStyle::drawControl(element, option, painter, widget);
    }
    return;
  }
  QColor shadow = option->palette.dark().color();
  shadow.setAlpha(120);
  switch (element) {
    case CE_DockWidgetTitle:
      if (sm_appThemed && sm_explorerStyle) {
        if (const QStyleOptionDockWidget *dwOpt =
                    qstyleoption_cast<const QStyleOptionDockWidget *>(option)) {
          painter->save();
          QStyleOptionDockWidget adjustedOpt = *dwOpt;
          QRect rect = option->rect.adjusted(0, 1, -1, -2);
          adjustedOpt.palette.setBrush(QPalette::All, QPalette::Dark,
                                       Qt::transparent);
          painter->save();
          painter->setClipRect(rect);
          drawRebarBackground(rect.adjusted(0, 0, 1, 1), painter);
          painter->restore();
          painter->setPen(shadow);
          painter->drawRect(rect);
          if (sm_vista) {
            QWindowsVistaStyle::drawControl(element, &adjustedOpt, painter,
                                            widget);
          } else {
            QWindowsXPStyle::drawControl(element, &adjustedOpt, painter,
                                         widget);
          }
          painter->restore();
        }
        break;
      } //fall through
    case CE_MenuBarItem:
      if (sm_appThemed && sm_explorerStyle) {
        if (const QStyleOptionMenuItem *mbi =
                      qstyleoption_cast<const QStyleOptionMenuItem *>(option)) {
          QStyleOptionMenuItem adjustedItem = *mbi;
          adjustedItem.palette.setBrush(QPalette::All, QPalette::Dark, shadow);
          adjustedItem.palette.setBrush(QPalette::All, QPalette::Button,
                                          Qt::NoBrush);
          if (sm_vista) {
            QWindowsVistaStyle::drawControl(element, &adjustedItem, painter,
                                            widget);
          } else {
            QWindowsXPStyle::drawControl(element, &adjustedItem, painter,
                                         widget);
          }
        }
        break;
      } //fall through
    case CE_MenuBarEmptyArea:
      if (sm_appThemed && sm_explorerStyle) {
        if (const QStyleOptionMenuItem *mbi =
                      qstyleoption_cast<const QStyleOptionMenuItem *>(option)) {
          QStyleOptionMenuItem adjustedItem = *mbi;
          adjustedItem.palette.setBrush(QPalette::All, QPalette::Dark, shadow);
          adjustedItem.palette.setBrush(QPalette::All, QPalette::Button,
                                        Qt::NoBrush);
          if (sm_vista) {
            QWindowsVistaStyle::drawControl(element, &adjustedItem, painter,
                                            widget);
          } else {
            QWindowsXPStyle::drawControl(element, &adjustedItem, painter,
                                         widget);
          }
        }
        break;
      } //fall through
    case CE_ToolBar:
      if (sm_appThemed && sm_explorerStyle) {
        if (const QStyleOptionToolBar *toolbar =
                       qstyleoption_cast<const QStyleOptionToolBar *>(option)) {
          QStyleOptionToolBar adjustedToolBar = *toolbar;
          adjustedToolBar.palette.setBrush(QPalette::All, QPalette::Dark,
                                           shadow);
          if (sm_vista) {
            QWindowsVistaStyle::drawControl(element, &adjustedToolBar, painter,
                                            widget);
          } else {
            QWindowsXPStyle::drawControl(element, &adjustedToolBar, painter,
                                         widget);
          }
        }
        break;
      } //fall through
    default:
      if (sm_vista) {
        QWindowsVistaStyle::drawControl(element, option, painter, widget);
      } else {
        QWindowsXPStyle::drawControl(element, option, painter, widget);
      }
      break;
  }
}

/*!
 * \inheritDoc
 */
void QWindowsNativeStyle::drawComplexControl(ComplexControl control,
                                             const QStyleOptionComplex *option,
                                             QPainter *painter,
                                             const QWidget *widget) const
{
  if (sm_vista) {
    QWindowsVistaStyle::drawComplexControl(control, option, painter, widget);
  } else {
    if (!sm_patchDisabled) {
      switch(control) {
        case CC_TitleBar: {
          painter->setFont(titleBarFont);
          QWindowsXPStyle::drawComplexControl(control, option, painter, widget);
          return;
        }
        default:
          break;
      }
    }
    QWindowsXPStyle::drawComplexControl(control, option, painter, widget);
  }
}

/*!
 * \inheritDoc
 */
QSize QWindowsNativeStyle::sizeFromContents(ContentsType type,
                                            const QStyleOption *option,
                                            const QSize &size,
                                            const QWidget *widget) const
{
  if (sm_vista) {
    return QWindowsVistaStyle::sizeFromContents(type, option, size, widget);
  }
  return QWindowsXPStyle::sizeFromContents(type, option, size, widget);
}

/*!
 * \inheritDoc
 */
QRect QWindowsNativeStyle::subElementRect(SubElement element,
                                          const QStyleOption *option,
                                          const QWidget *widget) const
{
  if (sm_vista) {
    return QWindowsVistaStyle::subElementRect(element, option, widget);
  }
  return QWindowsXPStyle::subElementRect(element, option, widget);
}

/*!
 * \inheritDoc
 */
QRect QWindowsNativeStyle::subControlRect(ComplexControl control,
                                          const QStyleOptionComplex *option,
                                          SubControl subControl,
                                          const QWidget *widget) const
{
  if (sm_vista) {
    return QWindowsVistaStyle::subControlRect(control, option, subControl,
                                              widget);
  }
  return QWindowsXPStyle::subControlRect(control, option, subControl, widget);
}

/*!
 * \inheritDoc
 */
QStyle::SubControl QWindowsNativeStyle::hitTestComplexControl(
                      ComplexControl control, const QStyleOptionComplex *option,
                      const QPoint &position, const QWidget *widget) const
{
  if (sm_vista) {
    return QWindowsVistaStyle::hitTestComplexControl(control, option, position,
                                                     widget);
  } 
  return QWindowsXPStyle::hitTestComplexControl(control, option, position,
                                                widget);
}

/*!
 * \inheritDoc
 */
int QWindowsNativeStyle::pixelMetric(PixelMetric metric,
                                     const QStyleOption *option,
                                     const QWidget *widget) const
{
  int pm;
  if (sm_vista) {
    pm = QWindowsVistaStyle::pixelMetric(metric, option, widget);
  }
  pm = QWindowsXPStyle::pixelMetric(metric, option, widget);
  if (!sm_patchDisabled) {
    switch (metric) {
      case PM_MDIMinimizedWidth:
        if (!sm_appThemed) {
          pm -= 20;
        }
        break;
      default:
        break;
    }
  }
  return pm;
}

/*!
 * \inheritDoc
 */
int QWindowsNativeStyle::styleHint(StyleHint hint, const QStyleOption *option,
                                   const QWidget *widget,
                                   QStyleHintReturn *returnData) const
{
  if (sm_vista) {
    return QWindowsVistaStyle::styleHint(hint, option, widget, returnData);
  }
  if (!(sm_patchDisabled || sm_appThemed)) {
    switch(hint) {
      case SH_ComboBox_PopupFrameStyle:
        return QFrame::Panel | QFrame::Plain;
      default:
        break;
    }
  }
  return QWindowsXPStyle::styleHint(hint, option, widget, returnData);
}

/*!
 * \inheritDoc
 */
QPalette QWindowsNativeStyle::standardPalette() const
{
  if (sm_vista) {
    return QWindowsVistaStyle::standardPalette();
  } else {
    return QWindowsXPStyle::standardPalette();
  }
}    

/*!
 * \inheritDoc
 */
QPixmap QWindowsNativeStyle::standardPixmap(StandardPixmap standardPixmap,
                                            const QStyleOption *opt,
                                            const QWidget *widget) const
{
  if (sm_vista) {
    return QWindowsVistaStyle::standardPixmap(standardPixmap, opt, widget);
  }
  return QWindowsXPStyle::standardPixmap(standardPixmap, opt, widget);
}

/*!
 * \inheritDoc
 */
void QWindowsNativeStyle::polish(QApplication *app)
{
  if (sm_vista) {
    QWindowsVistaStyle::polish(app);
  } else {
    QWindowsXPStyle::polish(app);
  }
}

/*!
 * \inheritDoc
 */
void QWindowsNativeStyle::unpolish(QApplication *app)
{
  if (sm_vista) {
    QWindowsVistaStyle::unpolish(app);
  } else {
    QWindowsXPStyle::unpolish(app);
  }
}

/*!
 * \inheritDoc
 */
void QWindowsNativeStyle::polish(QWidget *widget)
{
  if (sm_explorerStyle) {
    QMainWindow *window = qobject_cast<QMainWindow *>(widget);
    if (window) {
      window->setAttribute(Qt::WA_StyledBackground, true);
    }
  }
  if (sm_vista) {
    QWindowsVistaStyle::polish(widget);
  } else {
    QWindowsXPStyle::polish(widget);
  }
}

/*!
 * \inheritDoc
 */
void QWindowsNativeStyle::unpolish(QWidget *widget)
{
  if (sm_vista) {
    QWindowsVistaStyle::unpolish(widget);
  } else {
    QWindowsXPStyle::unpolish(widget);
  }
}

/*!
 * \inheritDoc
 */
void QWindowsNativeStyle::polish(QPalette pal)
{
  if (sm_vista) {
    QWindowsVistaStyle::polish(pal);
  } else {
    QWindowsXPStyle::polish(pal);
  }
}

// QWindowsNativeStyle :: Private Static Data Members. -------------------------

bool QWindowsNativeStyle::sm_initialized = false;
bool QWindowsNativeStyle::sm_vista = false;
bool QWindowsNativeStyle::sm_appThemed = false;
bool QWindowsNativeStyle::sm_patchDisabled = false;
bool QWindowsNativeStyle::sm_explorerStyle = false;
QFont QWindowsNativeStyle::titleBarFont;
